package nl.unimaas.group11.project1_3.algo;

import java.util.TimerTask;
import java.util.Timer;

import javax.swing.JFrame;

/*
 A: 1.0 x 1.0 x 2.0
 B: 1.0 x 1.5 x 2.0
 C: 1.5 x 1.5 x 1.5
 cargospace: 16.5 long, 2.5 wide, 4.0 high
 2x
 A: 2x2x4 value 3
 B: 2x3x4 value 3
 C: 3x3x3
 Cont: 33x5x8
 */

public class Bruteforce extends JFrame {
	private static final long serialVersionUID = 1L;

	public int[][][] container;
	public int[][][] bestSolution;
	private boolean found = false;
	private boolean timeUp = false;
	private double maxPercent = 0;
	public double maxValue = 0;
	private Timer timer;
	public long startTime;
	public long endTime;

	private int totalValue = 0;

	public int containerX, containerY, containerZ;

	public int[][] boxes = {
			// {box number,xDim,yDim,zDim}

			// Box A, 3 combos
			{ 1, 2, 4, 2 }, { 2, 2, 2, 4 }, { 3, 4, 2, 2 },

			// Box B, 6 combos
			{ 4, 4, 3, 2 }, { 5, 4, 2, 3 }, { 6, 2, 4, 3 }, { 7, 3, 4, 2 }, { 8, 2, 3, 4 }, { 9, 3, 2, 4 },

			// Box C, 1 Combo
			{ 10, 3, 3, 3 } };

	private int[] boxesValues;
	int[][] pentos = { { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0 }, // L

			{ 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, -1, 3, 0, 0, 3, 0 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 1, 3, 0 },

			{ 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 3, 1, 0 },

			{ 1, 3, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 3, 1, 0 },

			{ 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 3, 1, 0 },

			{ 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 0, 1, 0 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 3, 1, },

			{ 1, 0, 3, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 3, 1 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 0, 1, },

			{ 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 3, 1 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3 },

			{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3 },

			{ 1, 0, 1, 0, 0, 1, 1, 0, 1, 2, 0, 0, 3, 0, 1, 3 },

			{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 1, 3 },

			{ 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 1 },

			{ 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1 },

			{ 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 3, 0, 1 },

			{ 1, 3, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1 },

			{ 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 3 },

			{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3 },

			{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 1, 0, 3 },

			{ 1, 1, 0, 0, 1, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 3 },

			{ 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 2, 0 }, // P

			{ 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0 },

			{ 2, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 1, 2, 0 },

			{ 2, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 1, 2, 0 },

			{ 2, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0 },

			{ 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0 },

			{ 2, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 2, 1, 0 },

			{ 2, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0 },

			{ 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1 },

			{ 2, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1 },

			{ 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 2, 1 },

			{ 2, 0, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1 },

			{ 2, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 2 },

			{ 2, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 1, 2 },

			{ 2, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2 },

			{ 2, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 1, 2 },

			{ 2, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 2 },

			{ 2, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2 },

			{ 2, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 2 },

			{ 2, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 1, 0, 2 },

			{ 2, 1, 0, 0, 2, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1 },

			{ 2, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1 },

			{ 2, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 1, 2, 0, 1 },

			{ 2, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 1, 1, 0, 1 },

			{ 3, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 1, 2, 0 }, // T

			{ 3, 2, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 2, 2, 0 },

			{ 3, 1, 0, 0, 1, 1, 0, 0, 2, 0, 1, 2, 0, 2, 2, 0 },

			{ 3, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 0, 2, 0 },

			{ 3, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 0, 2 },

			{ 3, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 1, 2 },

			{ 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 0, 2, 2 },

			{ 3, 0, 1, 0, 0, 1, 1, 0, 0, 2, 0, 1, 2, 0, 2, 2 },

			{ 3, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 1, 1, 0, 2 },

			{ 3, 2, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1, 2, 0, 2 },

			{ 3, 1, 0, 0, 1, 0, 1, 0, 0, 2, 1, 0, 2, 2, 0, 2 },

			{ 3, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1, 0, 0, 2 }

	};

	// constructor
	public Bruteforce(int boxAValue, int boxBValue, int boxCValue, int containerX, int containerY, int containerZ) {

		// set up stuff
		boxesValues = new int[3];
		boxesValues[0] = boxAValue;
		boxesValues[1] = boxBValue;
		boxesValues[2] = boxCValue;

		this.containerX = containerX;
		this.containerY = containerY;
		this.containerZ = containerZ;
		container = new int[containerX][containerY][containerZ];
		bestSolution = new int[containerX][containerY][containerZ];

	}

	public void start(boolean pents, boolean isTimer, int seconds) {

		if (isTimer) {
			timer = new Timer();
			timer.schedule(new TimerEnd(), seconds * 1000);
		}

		startTime = System.currentTimeMillis();

		if (pents)
			backtrackingPento(nextFreeCoord());
		else
			backtracking(nextFreeCoord());

	}

	class TimerEnd extends TimerTask {
		public void run() {
			timeUp = true;
			timer.cancel(); // Terminate the timer
		}
	}

	// public methods
	public int[][][] returnSolution() {
		return container;
	}

	private int[][][] copy3DArray(int[][][] container) {
		int[][][] clonedArray = new int[containerX][containerY][containerZ];

		for (int x = 0; x < container.length; x++) {
			for (int y = 0; y < container[0].length; y++) {
				for (int z = 0; z < container[0][0].length; z++) {
					clonedArray[x][y][z] = (int) container[x][y][z];
				}
			}
		}
		return clonedArray;
	}

	private void backtracking(int[] coord) {
		OuterLoop: for (int boxNumber = 1; boxNumber <= 10; boxNumber++) {
			if (doesBoxFit(boxNumber, coord) && !timeUp) {
				placeBox(boxNumber, coord);

				// printContainer();
				double percent = returnPercentFilled(container);
				if (percent > maxPercent) {
					maxPercent = percent;
				}

				if (totalValue > maxValue) {
					maxValue = totalValue;
					endTime = System.currentTimeMillis();
					bestSolution = copy3DArray(container);
				}

				if (nextFreeCoord() == null) {
					// optimal solution has been found
					found = true;
					break OuterLoop;
				} else {
					backtracking(nextFreeCoord());
				}

				removeBox(boxNumber, coord);
			}
		}
	}

	private void backtrackingPento(int[] coord) {
		OuterLoop: for (int boxNumber = 0; boxNumber < pentos.length; boxNumber++) {
			if (doesPentoFit(boxNumber, coord)) {
				placePento(boxNumber, coord);

				double percent = returnPercentFilled(container);
				if (percent > maxPercent) {
					maxPercent = percent;
					endTime = System.currentTimeMillis();
					bestSolution = copy3DArray(container);
				}

				if (nextFreeCoord() == null) {
					found = true;
					break OuterLoop;

				} else {
					backtrackingPento(nextFreeCoord());
				}

				if (!found)
					removePento(boxNumber, coord);
			}

		}

	}

	private void removeBox(int boxNumber, int[] Coord) {
		for (int x = 0; x < boxes[(boxNumber - 1)][1]; x++) {
			for (int y = 0; y < boxes[(boxNumber - 1)][2]; y++) {
				for (int z = 0; z < boxes[(boxNumber - 1)][3]; z++) {
					container[x + Coord[0]][y + Coord[1]][z + Coord[2]] = 0;
				}
			}
		}

		if (boxes[(boxNumber - 1)][0] == 1 || boxes[(boxNumber - 1)][0] == 2 || boxes[(boxNumber - 1)][0] == 3) {
			totalValue -= boxesValues[0];
		} else if (boxes[(boxNumber - 1)][0] == 4 || boxes[(boxNumber - 1)][0] == 5 || boxes[(boxNumber - 1)][0] == 6 || boxes[(boxNumber - 1)][0] == 7 || boxes[(boxNumber - 1)][0] == 8 || boxes[(boxNumber - 1)][0] == 9) {
			totalValue -= boxesValues[1];
		} else {
			totalValue -= boxesValues[2];
		}

	}

	public void removePento(int x, int[] coordinates) {
		int[][] input = pentos;
		for (int a = 1; (input[x].length) > a; a = a + 3) {
			container[pentos[x][a + 1] + coordinates[0]][pentos[x][a] + coordinates[1]][pentos[x][a + 2] + coordinates[2]] = 0;
		}
	}

	private int[] nextFreeCoord() {
		for (int x = 0; x < container.length; x++) {
			for (int y = 0; y < container[0].length; y++) {
				for (int z = 0; z < container[0][0].length; z++) {
					if (container[x][y][z] == 0) {
						int[] freeCoord = { x, y, z };
						return freeCoord;
					}
				}
			}
		}
		return null;
	}

	private void placeBox(int boxNumber, int[] Coord) {

		for (int x = 0; x < boxes[(boxNumber - 1)][1]; x++) {
			for (int y = 0; y < boxes[(boxNumber - 1)][2]; y++) {
				for (int z = 0; z < boxes[(boxNumber - 1)][3]; z++) {

					container[x + Coord[0]][y + Coord[1]][z + Coord[2]] = boxes[(boxNumber - 1)][0];
				}
			}
		}

		if (boxes[(boxNumber - 1)][0] == 1 || boxes[(boxNumber - 1)][0] == 2 || boxes[(boxNumber - 1)][0] == 3) {
			totalValue += boxesValues[0];
		} else if (boxes[(boxNumber - 1)][0] == 4 || boxes[(boxNumber - 1)][0] == 5 || boxes[(boxNumber - 1)][0] == 6 || boxes[(boxNumber - 1)][0] == 7 || boxes[(boxNumber - 1)][0] == 8 || boxes[(boxNumber - 1)][0] == 9) {
			totalValue += boxesValues[1];
		} else {
			totalValue += boxesValues[2];
		}

	}

	private boolean doesBoxFit(int boxNumber, int[] Coord) {

		boolean allEmpty = true;

		OuterLoop: while (allEmpty) {

			for (int x = 0; x < boxes[(boxNumber - 1)][1]; x++) {
				for (int y = 0; y < boxes[(boxNumber - 1)][2]; y++) {
					for (int z = 0; z < boxes[(boxNumber - 1)][3]; z++) {

						try {
							if (container[x + Coord[0]][y + Coord[1]][z + Coord[2]] != 0) {
								allEmpty = false;
								break OuterLoop;
							}
						} catch (ArrayIndexOutOfBoundsException e) {
							allEmpty = false;
							break OuterLoop;
						}

					}
				}
			}

			break OuterLoop;

		}
		return allEmpty;

	}

	private boolean doesPentoFit(int x, int[] coordinates) {

		for (int a = 1; (pentos[x].length) > a; a = a + 3) {
			try {
				if (container[pentos[x][a + 1] + coordinates[0]][pentos[x][a] + coordinates[1]][pentos[x][a + 2] + coordinates[2]] != 0) {
					return false;
				}
			}

			catch (ArrayIndexOutOfBoundsException e) {
				return false;
			}

		}
		return true;
	}

	public void placePento(int x, int[] coordinates) {
		
		for (int a = 1; (pentos[x].length) > a; a = a + 3) {
			container[pentos[x][a + 1] + coordinates[0]][pentos[x][a] + coordinates[1]][pentos[x][a + 2] + coordinates[2]] = pentos[x][0];
		}

	}



	public double returnPercentFilled(int[][][] container) {

		int totalVolume = (container.length * container[0].length * container[0][0].length);
		int unitsFilled = 0;

		for (int x = 0; x < container.length; x++) {
			for (int y = 0; y < container[0].length; y++) {
				for (int z = 0; z < container[0][0].length; z++) {
					if (container[x][y][z] != 0) {
						unitsFilled++;
					}
				}
			}
		}
		return (((double) unitsFilled) / totalVolume) * 100;
	}

	

}
